package XML与JSON.使用DOM;

import java.io.File;
import java.io.IOException;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;

import org.w3c.dom.DOMException;
import org.w3c.dom.Document;
import org.w3c.dom.Node;
import org.w3c.dom.traversal.DocumentTraversal;
import org.w3c.dom.traversal.NodeFilter;
import org.w3c.dom.traversal.NodeIterator;
import org.w3c.dom.traversal.TreeWalker;
import org.xml.sax.SAXException;
/*
<?xml version="1.0" encoding="UTF-8"?>
<div id="root">
    <p>hello</p>
    <div>               
        <p>world</p>
        <ul>
            <li>
                <p>html</p>
                <p>html1</p>
            </li>
        </ul>
    </div>
</div>
 */

public class DOM使用NodeIterator {
	public static void main(String[] args) throws ParserConfigurationException, SAXException, IOException {
		File file = new File("src\\main\\java\\XML与JSON\\使用DOM\\test.xml");
		DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
		DocumentBuilder loader = factory.newDocumentBuilder();
		Document document = loader.parse(file);

		DocumentTraversal trav = (DocumentTraversal) document;
		Node root =document.getDocumentElement();
		//String nodeName=root.getNodeName().toLowerCase();
		
		NodeFilter filter = new NodeFilter(){
			@Override
			public short acceptNode(Node n) {
			   return n.getNodeName().toLowerCase()=="p" ? NodeFilter.FILTER_ACCEPT:NodeFilter.FILTER_REJECT;//或者NodeFilter.FILTER_REJECT
			}
		};
		//使用NodeIterator对象时，NodeFilter.FILTER_SKIP和NodeFilter.FILTER_REJECT的作用是相同的：跳过指定的节点；但是，在使用TreeWalker对
		//象时，NodeFilter.FILTER_SKIP会跳过相应的子节点继续前进到子树的下个节点，而NodeFilter.FILTER_REJECT则会跳过以及该节点的整个子树
		NodeIterator it = trav.createNodeIterator(root, NodeFilter.SHOW_ALL, null, true);//显示所有p
//		NodeIterator it = trav.createNodeIterator(root, NodeFilter.SHOW_ELEMENT, filter, true);//显示所有p
		//TreeWalker it = trav.createTreeWalker(root, NodeFilter.SHOW_ELEMENT, filter, true);//只显示一个p,差别很明显，当指针遍历第一个P节点后，紧接着是DIV节点，而NodeFilter.FILTER_REJECT则会跳过DIV节点以及该节点的整个子树。
		int c = 1;
		for (Node node = it.nextNode(); node != null; node = it.nextNode()) {
			String name = node.getNodeName();
			System.out.printf("%d %s%n", c, name);
			c++;
			String text = node.getTextContent().trim();

         if (!text.isEmpty()) {
             System.out.println(text);
         }

		}
	}
}
/*
该示例打印continents.xml文件的所有节点元素。
DocumentTraversal trav = (DocumentTraversal) document;
从文档中，我们得到一个DocumentTraversal对象。
NodeIterator it = trav.createNodeIterator(document.getDocumentElement(), NodeFilter.SHOW_ELEMENT, null, true);

public NodeIterator createNodeIterator(Node root,
                                       int whatToShow,
                                       NodeFilter filter,
                                       boolean entityReferenceExpansion) throws DOMException;
【参数说明：】
1.root：想要作为搜索起点的树中的节点；
2.whatToShow：表示要访问哪些节点的数字代码；
3.filter：是一个NodeFilter对象，或者一个表示应该接受还是拒绝某种特定节点的函数；可以不指定即为null；
4.EntityReferenceExpansion：布尔值，表示是否扩展实体应用。这个参数在HTML中没有用，因为其中的实体引用不能扩展。

【whatToShow】
其中，whatToShow是一个位掩码，这个参数的值以常量形式在NodeFilter类型中定义，下面是常用的几个值：

NodeFilter.SHOW_ALL：显示所有类型的节点 
NodeFilter.SHOW_ELEMENT：显示元素节点 
NodeFilter.SHOW_ATTRIBUTE：显示特性节点。由于DOM结构原因，实际上不能使用这个值 
NodeFilter.SHOW_TEXT：显示文本节点 
NodeFilter.SHOW_CDATA_SECTION：显示CDATA节点。对HTML页面没有用 
NodeFilter.SHOW_ENTITY_REFERENCE：显示实体引用节点。对HTML页面没有用 
NodeFilter.SHOW_ENTITYE：显示实体节点。对HTML页面没有用 
NodeFilter.SH0W_PROCESSING_INSTRUCTION：显示处理指令节点。对HTML页面没有用 
NodeFi1ter.SHOW_COMMENT：显示注释节点 
NodeFilter.SHOW_DOCUMENT：显示文档节点 
NodeFilter.SHOW_DOCUMENT_TYPE：显示文档类型节点 
NodeFilter.SHOW_DOCUMENT_FRAGMENT：显示文档片段节点。对HTML页面没有用 
NodeFilter.SHOW_NOTATION：显示符号节点。对HTML页面没有用

【NodeFilter】
每个NodeFilter对象只有一个方法，那就是acceptNode()；
pubblic interface NodeFilter {
    public static final short FILTER_ACCEPT             = 1;
    public static final short FILTER_REJECT             = 2;
    public static final short FILTER_SKIP               = 3;
    public static final int SHOW_ALL                  = 0xFFFFFFFF;
    public static final int SHOW_ELEMENT              = 0x00000001;
    public static final int SHOW_ATTRIBUTE            = 0x00000002;
    public static final int SHOW_TEXT                 = 0x00000004;
    public static final int SHOW_CDATA_SECTION        = 0x00000008;
    public static final int SHOW_ENTITY_REFERENCE     = 0x00000010;
    public static final int SHOW_ENTITY               = 0x00000020;
    public static final int SHOW_PROCESSING_INSTRUCTION = 0x00000040;
    public static final int SHOW_COMMENT              = 0x00000080;
    public static final int SHOW_DOCUMENT             = 0x00000100;
    public static final int SHOW_DOCUMENT_TYPE        = 0x00000200;
    public static final int SHOW_DOCUMENT_FRAGMENT    = 0x00000400;
    public static final int SHOW_NOTATION             = 0x00000800;
    public short acceptNode(Node n);
}
如果要访问给定的节点，该方法返回NodeFilter.FILTER_ACCEPT；

如果要跳过访问给定的节点，该方法返回NodeFilter.FILTER_REJECT或者NodeFilter.FILTER_SKIP

1.使用对象方式创建一个filter过滤器：
var filter = {
    acceptNode:function(node){
        return node.tagName.toLowerCase() == 'p' ?
        NodeFilter.FILTER_ACCEPT:
        NodeFilter.FILTER_SKIP;   //或者NodeFilter.FILTER_REJECT
    }
}

var root = document.getElementById('root');
var iterator = document.createNodeIterator(root, NodeFilter.SHOW_ELEMENT, filter, false);

2.或者使用匿名函数方式，创建一个filter过滤器
var filter = function(node){
    return node.tagName.toLowerCase() == 'p' ?
    NodeFilter.FILTER_ACCEPT:
    NodeFilter.FILTER_REJECT;    //或者NodeFilter.FILTER_SKIP
}

var root = document.getElementById('root');
var iterator = document.createNodeIterator(root, NodeFilter.SHOW_ELEMENT, filter, false);

上面两种方式，就可以创建一个NodeIterator节点迭代器，当然了，如果不指定过滤器filter，也可以传入null，比如：
var iterator = document.createNodeIterator(document, NodeFilter.SHOW_ALL, null, false);

这样生成的NodeIterator就可以访问所有节点。

NodeIterator的主要方法应用：

NodeIterator的主要方法是nextNode()和previousNode()。

在深度优先的DOM子树遍历中，nextNode()用于向前前进一步，previousNode()用于向后后退一步。
在遍历到DOM子树的最后一个节点时，nextNode()返回null；在previousNode()返回根节点之后，再次调用previousNode()也会返回null。


                                        
我们创建一个NodeIterator。 设置NodeFilter.SHOW_ELEMENT时，仅显示节点元素。
for (Node node = it.nextNode(); node != null;node = it.nextNode()) {
    String name = node.getNodeName();
    System.out.printf("%d %s%n", c, name);
    c++;
}
在 for 循环中，我们遍历节点并打印其名称。

【TreeWalker与NodeIterator中filter区别】

使用NodeIterator对象时，NodeFilter.FILTER_SKIP和NodeFilter.FILTER_REJECT的作用是相同的：跳过指定的节点；但是，在使用TreeWalker对
象时，NodeFilter.FILTER_SKIP会跳过相应的子节点继续前进到子树的下个节点，而NodeFilter.FILTER_REJECT则会跳过以及该节点的整个子树

NodeIterator

getRoot()
getWhatToShow()
getFilter()
getExpandEntityReferences()
nextNode()
previousNode()
detach()

TreeWalker                         NodeIterator

getRoot()                          getRoot()
getWhatToShow()                    getWhatShow()
getFilter()                        getFilter()
getExpandEntityReferences()        getExpandEntityReferences()
getCurrentNode()
setCurrentNode(Node)
parentNode()
firstChild()
lastChild()
previousSibling()
nextSibling()
previousNode()                     previousNode()
nextNode()                         nextNode()
                                   detach()  已弃用

TreeWalker增加的方法：
getCurrentNode():
setCurrentNode(Node):
parentNode()：遍历到当前节点的父节点；
firstChild()：遍历到当前节点的第一个子节点；
lastChild()：遍历到当前节点的最后一个子节点；
nextSibling()：遍历到当前节点的下一个同辈节点；
previousSibling()：遍历到当前节点的上一个同辈节点；

*/
